﻿//
// <author>David Nohejl</author>
//
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using WPFTrees.DataStructures;
using WPFTrees.Parser;
using System.Windows.Controls;
using System.Windows;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Diagnostics;

namespace WPFTrees.Layouts
{
    /// <summary>
    /// Styl ktery prideluje postromum kruhovou vysec podle poctu uzlu.
    /// </summary>
    class ClassicRadialLayout:LayoutBase,ICanUseBranchLengths,
        ISupportUnrooting,ISupportGroups
    {
        NTreeNode<NewickNode> _root;
        Canvas _canvas;
        double radius = 50.0;
        double unit = 0.0;        

        /// <summary>
        /// Initialize new instance of ClassicRadialLayout.
        /// </summary>
        public ClassicRadialLayout()
        {                   
            _branchDrawer = new StraightLineBranchDrawer(Brushes.Black);
        }

        /// <inheritdoc />
        public override void ExecuteLayout(Canvas canvas, NTree<NewickNode> tree)
        {
            Tree = tree;
            _canvas = canvas;
            double width = canvas.RenderSize.Width;
            double height = canvas.RenderSize.Height;
            unit = (Math.Min(height, width) / 2) / _tree.Info.MaxDepthInUnits;
            _root = tree.GetRoot();
            // umisti koren do stredu canvasu
            Point rootPos = new Point(width / 2, height / 2);

            FrameworkElement element = _root.Value().VisualElement;
            // schova koren pokud kreslime nezakoreneny strom
            element.Visibility = IsRooted 
                ? Visibility.Visible 
                : Visibility.Collapsed;

            Canvas.SetLeft(element, rootPos.X);
            Canvas.SetTop(element, rootPos.Y);

            double wedge = Math.PI*2;
            double tau = 0;
            ExecuteLayoutRecursive(_root,_root, rootPos, wedge, tau,0,0);
        }
        
        /// <summary>
        /// 
        /// </summary>
        /// <param name="root"></param>
        /// <param name="node"></param>
        /// <param name="rootPos">pozice korene podstromu</param>
        /// <param name="wedgeParent">kruhova vysec pro tento podstrom</param>
        /// <param name="tau">uhel pootoceni</param>
        /// <param name="grandParentTau">Uhle pootoceni predka.</param>
        /// <param name="grandParentWedge">Kruhova vysec pootoceni predka.</param>
        void ExecuteLayoutRecursive(NTreeNode<Parser.NewickNode> root, 
            NTreeNode<Parser.NewickNode> node, Point rootPos, double wedgeParent,
            double tau, double grandParentWedge, double grandParentTau)
        {
            Point newPos = rootPos;
            if (node != _root)
            {                
                double branchLenght = (bUseBranchLenghts 
                        && node.Value().BranchLenght.HasValue) 
                    ? unit * node.Value().BranchLenght.Value 
                    : radius;

                // spocita pozici uzlu
                Vector x = new Vector(rootPos.X,rootPos.Y);
                Vector y = new Vector(Math.Cos(tau + (wedgeParent / 2)),
                                      Math.Sin(tau + (wedgeParent / 2)));
                Vector newPosVector = x+ branchLenght*y;
                
                newPos.X = newPosVector.X;
                newPos.Y = newPosVector.Y;

                FrameworkElement element = node.Value().VisualElement;

                // nastavi pozici uzlu
                Canvas.SetLeft(element, newPos.X);
                Canvas.SetTop(element, newPos.Y);
               
                // nastavi viditelnost labelu
                Tools.TreeTools.DetermineLabelVisibility(node.Value());

                // otoci label
                #region rotation
                Canvas nodeCanvas = (Canvas)node.Value().VisualElement;
                Label label = nodeCanvas.Children[1] as Label;
                double angle = tau + (wedgeParent / 2);
                // to degrees                
                angle = angle / (2 * Math.PI) * 360;
                // natoceni ve smeru vetve                    
                Matrix m1 = new RotateTransform(angle, 0.5, 0.5).Value;
                Matrix prod = m1;
                if ((Math.Abs(angle) >= 90) && (Math.Abs(angle) <= 270))
                {
                    // zrcadlove prevraceni                    
                    Matrix m2 = new ScaleTransform(-1, -1, 0, 0).Value;
                    label.LayoutTransform = new MatrixTransform(m2);                    
                }
                Debug.Print(string.Format("{1} {0}", angle, node.Value().Label));
                label.RenderTransform = new MatrixTransform(prod);
                
                #endregion

                // nakresli hranu
                _branchDrawer.PositionBranch(node.Value().BranchToParentElement,
                    root.Value(), node.Value(), rootPos, newPos);

            }


            double n = tau;

            #region group
            if (root.Value().GroupInfo != null)
            {               
                UpdateGroupShape(root,
                    rootPos,
                    grandParentTau,grandParentWedge);
            }
            #endregion    

            for (int i = 0; i < node.CountImmediateChildren; i++)
            {                
                NTreeNode<NewickNode> child = node.Children[i];
                int childLeaves = Math.Max(child.CountChildren, 1);
                double w = ((double)childLeaves / (double)_root.CountChildren) 
                           * Math.PI * 2;
                double  tauW = n;              

                ExecuteLayoutRecursive(node, child,newPos, w, tauW,
                    wedgeParent,tau);
                n = n + w;                                
            }
        }

        private void UpdateGroupShape(NTreeNode<Parser.NewickNode> root, 
            Point rootPos, double tau, double wedge)
        {
            // aktualizuje tvar graficke reprezentace skupiny
            // pro tento styl je to kruhova vysec, uhel je dany uhlem vysece 
            // pridelene podstromu algoritmem, a polomer je dany ??
            // TODOL popis algoritmu, komentare
            NewickNode node = root.Value();
            double multiplyRadius = MainWindow.ComputeGroupRadius(root, 
                radius, unit, bUseBranchLenghts);

            Vector x = new Vector(rootPos.X, rootPos.Y);
            Vector y = new Vector(Math.Cos(tau), Math.Sin(tau));
            Vector newGroupVector1 = x + multiplyRadius * y;

            y = new Vector(Math.Cos(tau+wedge), Math.Sin(tau+wedge));
            Vector newGroupVector2 = x + multiplyRadius * y;                     


            Path path = node.GroupInfo.Shape as Path;
            PathGeometry geometry = path.Data as PathGeometry;
            PathFigure figure = geometry.Figures[0];
            figure.StartPoint = rootPos;
            LineSegment lineSeg1 = figure.Segments[0] as LineSegment;

            lineSeg1.Point = new Point(newGroupVector1.X, newGroupVector1.Y);
            ArcSegment arcSeg = figure.Segments[1] as ArcSegment;

            double size = Point.Subtract(lineSeg1.Point, arcSeg.Point).Length;
            arcSeg.Size = new Size(multiplyRadius, multiplyRadius);
            arcSeg.RotationAngle = wedge*Math.PI*2/360;
            arcSeg.IsLargeArc = (wedge>Math.PI);
            arcSeg.SweepDirection = SweepDirection.Clockwise;
            arcSeg.Point = new Point(newGroupVector2.X, newGroupVector2.Y);
            LineSegment lineSeg2 = figure.Segments[2] as LineSegment;
            lineSeg2.Point = rootPos;

        }
       
        /// <inheritdoc />
        public Shape CreateGroupShape(NewickNode node)
        {
            Path path = new Path();
            PathGeometry geometry = new PathGeometry();
            PathFigure figure = new PathFigure();
            figure.Segments.Add(new LineSegment());
            figure.Segments.Add(new ArcSegment());
            figure.Segments.Add(new LineSegment());
            geometry.Figures.Add(figure);
            path.Data = geometry;
            path.Stroke = new SolidColorBrush(node.GroupInfo.Color);
            path.Fill = new SolidColorBrush(node.GroupInfo.Color);
            path.Opacity = 0.2;
            return path;
        }


        bool bUseBranchLenghts = true;

        /// <inheritdoc />
        public bool UseBranchLenghts
        {
            get
            {
                return bUseBranchLenghts;
            }
            set
            {
                bUseBranchLenghts = value;
            }
        }        

        /// <inheritdoc />
        bool ISupportUnrooting.IsRooted
        {
            get
            {
                return _isRooted;
            }
            set
            {
                _isRooted = value;
            }
        }
    }
}
